var xlsx = require("node-xlsx");

var excelTemplates = {};
var excelTemplateOptions = {};
var xlsxImportPath = "/slime/xlsx/import";

/*@lab(function(lab) {

    if (lab.redis.sessionManager) {
        @servlet.session(xlsxImportPath, lab.redis.sessionManager);
    } else {
        @servlet.session(xlsxImportPath);
    }

})*/

@servlet.get(xlsxImportPath, function(request, response) {
    var controller = this;
    return request.normalize().then(function() {

        var doExportTemplate = function(superAsync) {
            if (excelTemplates[request.form.name]) {
                controller.break();
                var sheetData = {
                    name: request.form.name,
                    data: [@.keys(excelTemplates[request.form.name])]
                };
                response.headers["Content-Disposition"] = "attachment;filename=" + request.form.name + ".xlsx";
                response.headers["Content-Type"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                response.send(xlsx.build([sheetData])).pipe(superAsync);
            } else {
                superAsync.next();
            }
        }

        if (request.session.currentOU && request.session.currentUser) {
            doExportTemplate(this);
        } else if (request.headers["Authorization"]) {
            var Authorization = request.headers["Authorization"].split(" ");
            switch (Authorization[0]) {
                case "Basic":
                    {
                        var authorization = Buffer.from(Authorization[1], "base64").toString("utf-8").split(":");
                        if (authorization.length == 2) {
                            @lab(function(lab) {
                                if (!request.session.participantID) {
                                    request.session.participantID = @.uuid();
                                }

                                return lab.backend.user.login(request.session, {
                                    account: authorization[0],
                                    password: authorization[1],
                                })
                            }).finished((error) => {
                                if (error) {
                                    @error(error);
                                    this.reject(@.error.http(403));
                                } else {
                                    doExportTemplate(this);
                                }
                            })
                        } else {
                            throw @.error.http(403);
                        }
                        break;
                    }
                default:
                    {
                        throw @.error.http(403);
                    }
            }
        } else {
            response.headers["WWW-Authenticate"] = `Basic Realm="Login"`;
            throw @.error.http(401);
        }
    })
})

@servlet.post(xlsxImportPath, function(request, response) {

    this.break();
    return request.normalize().then(function() {
        @.async(function() {
            if (request.session.currentOU && request.session.currentUser) {
                this.next(request.session.currentUser);
            } else {
                throw @lab.errors.not_login;
            }

        }).then(function(currentUser) {

            return @lab(function(lab) {

                var excelImportResult = lab.slime.core.@importAsExcel(request.form.name, request.form.file.file);

                return @.async(function() {

                    if (request.form.action) {

                        lab.doAction(request.form.action, [request.session, @.merge.simple(request.form, {
                            file: excelImportResult,
                            filename: request.form.file.name
                        })]).pipe(this);

                    } else {
                        this.next(excelImportResult);
                    }

                }).then(function(result) {

                    lab.create("slime_xlsx_imports", {
                        "name": request.form.name,
                        "uid": request.session.currentUser.id,
                        "data": excelImportResult,
                        "action": request.form.action,
                        "create_date": Date.now()
                    }).resolve(result).pipe(this);

                })

            })
        }).finished((error, result) => {
            response.headers["Content-type"] = "application/json;charset=utf-8";
            if (error) {
                @error(error);
                response.send({
                    error: error.message
                }).pipe(this);
            } else {
                @dump(result)
                response.send({
                    result: result || null
                }).pipe(this);
            }
        })

    });

});



module.exports = function(lab) {
    return {
        @registerExcelTemplate: function(name, model, options = {}) {

            if (!name || !model) {
                throw @lab.errors.invalid_parameters;
            }

            if (excelTemplates[name]) {
                @warn(`xlsx template ${name} already exists`);
            }

            excelTemplateOptions[name] = options;
            excelTemplates[name] = Object.keys(model).reduce(function(result, field) {

                var definition = model[field];
                var idx_bracket = definition.indexOf("[");
                var idx_end_bracket = definition.indexOf("]");

                if (idx_end_bracket > 0 && idx_bracket > 0 && idx_end_bracket > idx_bracket) {

                    var array_idx = parseInt(definition.substr(idx_bracket + 1, idx_end_bracket - idx_bracket - 1));

                    if (isNaN(array_idx)) {
                        throw @lab.errors.invalid_parameters;
                    }

                    result[field] = {
                        valueType: "array",
                        alias: definition.substr(0, idx_bracket),
                        index: array_idx
                    }

                } else {

                    result[field] = {
                        valueType: "string",
                        alias: model[field]
                    }

                }
                return result;
            }, {});

        },
        @autoRegisterExcelTemplate: function(name, file, options) {

            if (!name || !file || !@.fs.exists.file(file)) {
                throw @lab.errors.invalid_parameters;
            }

            var workSheetsFromFile = xlsx.parse(file);

            var header = workSheetsFromFile[0].data[0];
            var alias = workSheetsFromFile[0].data[1] || [];

            lab.slime.core.@registerExcelTemplate(name, header.reduce(function(result, field, index) {
                result[field] = alias[index] || field;
                return result;
            }, {}), options);

        },
        @importAsExcel: function(name, file) {

            if (!excelTemplates[name]) {
                throw @lab.errors.object_not_found;
            }

            if (!@.fs.exists.file(file)) {
                throw @lab.errors.invalid_parameters;
            }

            var excelTemplate = excelTemplates[name];

            var sheetsFromFile = xlsx.parse(file);
            var data = sheetsFromFile[0].data;

            var idx_map = data[0].reduce(function(result, field, index) {

                if (excelTemplate[field]) {
                    result[index] = excelTemplate[field];
                } else {
                    @warn("field :" + field + " not found in excel template : " + name);
                }
                return result;
            }, {});
            @info("Start to import " + name);
            return data.slice(1).reduce(function(result, row) {
                result.push(row.reduce(function(row_result, value, index) {
                    var field = idx_map[index];
                    if (field) {
                        switch (field.valueType) {
                            case 'string':
                                {
                                    row_result[field.alias] = value;
                                    break;
                                }
                            case 'array':
                                {

                                    if (!row_result[field.alias]) {
                                        row_result[field.alias] = [];
                                    }
                                    row_result[field.alias][field.index] = value;
                                    break;
                                }
                        }
                    }
                    return row_result;
                }, {}));
                return result;
            }, []);
        },
        @exportExcelTemplateAsExcel: function(name) {
            if (excelTemplates[name]) {
                var sheetData = {
                    name: name,
                    data: [Object.keys(excelTemplates[name])]
                };
                return @.async.resolve(xlsx.build([sheetData]));
            } else {
                throw @lab.errors.object_not_found;
            }
        },
        @exportDataAsExcel: function({ title, model, data, upload = true }) {
            if (!model || @.is.nil(data)) {
                throw @lab.errors.invalid_parameters;
            }

            var modelData;
            var modelName = title;


            if (@.is(model, String)) {
                if (!modelName) {
                    modelName = model;
                }

                model = lab.models[model];
                if (!model) {
                    throw @lab.errors.invalid_parameters;
                }
            } else if (!@.is(model, Object)) {
                throw @lab.errors.invalid_parameters;
            }

            if (!modelName) {
                if (model["!label"]) {
                    modelName = model["!label"];
                } else {
                    modelName = `DATA_EXPORT_${@.format.date(new Date(),'YYYY-MM-DD')}`
                }
            }

            if (Array.isArray(data)) {
                modelData = data.filter(function(single) {
                    return !@.is.nil(single);
                }).map(function(single) {
                    return @.merge.advanced(model, single);
                })
            } else {
                modelData = [@.merge.advanced(model, data)];
            }
            var sheetData = {
                name: title || modelName,
                data: Object.keys(model).reduce(function(rows, field) {
                    var row = [model[field]["!label"] || field];
                    modelData.forEach(function(dataRow) {
                        if (@.is.nil(dataRow[field])) {
                            row.push("");
                        } else {
                            row.push(dataRow[field]);
                        }
                    })
                    rows.push(row);
                    return rows;
                }, [])
            };

            if (upload) {

                return lab.uploader.qiniu.@bufferUpload({
                    path: @.uuid() + "/" + modelName + ".xlsx",
                    buffer: xlsx.build([sheetData])
                });

            } else {

                return @.async.resolve(xlsx.build([sheetData]));

            }
        },
        @exportAsExcel: function({ name, title, headers, list, upload = true , fill = false }) {
            if ((!name && !fill && !headers) || (!name && !title)) {
                throw @lab.errors.invalid_parameters;
            }

            if (!headers) {
                if (excelTemplates[name]) {
                    headers = Object.keys(excelTemplates[name]).reduce(function(result, row) {
                        result[excelTemplates[name][row].alias] = row;
                        return result;
                    }, {});
                } else if (fill){
                    headers = {};
                } else {
                    throw @lab.errors.invalid_parameters;
                }
            }

            var headerKeys = Object.keys(headers);
            var sheetData = {
                name: title || name,
                data: []
            };

            var headerRow = [];
            headerKeys.forEach(function(key) {
                headerRow.push(headers[key]);
            });

            if (!fill){
                sheetData.data.push(headerRow);
            }
            

            (list || []).forEach(function(element) {

                var dataRow = [];
                headerKeys.forEach(function(key) {

                    if (@.is.nil(element[key])) {
                        dataRow.push("");
                    } else if (@.is(element[key],Number)) {
                        dataRow.push(element[key]);
                    } else {
                        dataRow.push(element[key].toString());
                    }

                });

                if (fill) {
                    Object.keys(element).forEach(function (key) {

                        if (@.is.nil(headers[key])){
    
                            headers[key] = key;
                            headerKeys.push(key);
                            headerRow.push(key);

                            if (@.is.nil(element[key])) {
                                dataRow.push("");
                            } else if (@.is(element[key],Number)) {
                                dataRow.push(element[key]);
                            } else {
                                dataRow.push(element[key].toString());
                            }
    
                        }
                    });
                }

                sheetData.data.push(dataRow);
            });

            if (fill){
                sheetData.data.unshift(headerRow);
            }

            if (upload) {

                return lab.uploader.qiniu.@bufferUpload({
                    path: @.uuid() + "/" + (name || title) + ".xlsx",
                    buffer: xlsx.build([sheetData])
                });

            } else {

                return @.async.resolve(xlsx.build([sheetData]));

            }

        }
    }
}